home *** CD-ROM | disk | FTP | other *** search
/ GFX Sensations 1 / Graphic Sensations - Volume 1.iso / tools / amiga / fractals / rm2.lha / rm2.c < prev    next >
Encoding:
Text File  |  1994-01-01  |  33.3 KB  |  1,035 lines

  1. /* 80 columns:
  2. 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234567890
  3. */
  4. /*
  5.  * denbeste@world.std.com : Steven C. Den Beste
  6.  *
  7.  * "rm2.c": This is based on the program "retmand" which someone else posted
  8.  * recently. I could not have done this without him, and I hereby announce
  9.  * my gratitude. Nonetheless, I suspect that this is so dramatically modified
  10.  * at this point that the original author wouldn't recognize it.
  11.  *
  12.  * The original program just drew the entire Mandelbrot drawing, using random
  13.  * colors each time. That's all it did - but to do that it had to implement
  14.  * the entire algorithm for CALCULATING it, and all the code
  15.  * for displaying stuff on the Retina. Those are the parts I couldn't have
  16.  * handled. In particular, it used the smart algorithm for calculating
  17.  * the Mandelbrot diagram (the one which optimizes square sections) instead
  18.  * of the stupid algorithm (the one which calculates each point every time).
  19.  *
  20.  * What I DO know how to do is enhance,
  21.  * clean up, and optimize. But to do that I had to
  22.  * perform some modifications. First and foremost: The original code was
  23.  * written for the SAS compiler. What I've got is
  24.  * Aztec C 5.0. Regrettably, they aren't compatible. For a while I tried
  25.  * to maintain compatibility with #ifdef's, but that got so complex that
  26.  * I finally gave up and deleted everything non-aztec, so this code is
  27.  * thoroughly MANXified now. Adapting this back to SAS is probably going to be
  28.  * a pain.
  29.  *
  30.  * The core of any Mandelbrot program is a routine which iterates a
  31.  * rather strange function, waiting until the value of the function
  32.  * exceeds a certain threshold. The value it returns is the integer
  33.  * count of the number of loops it took for this to take place. All the
  34.  * calculations are done in floating point. As a result, Mandelbrot programs
  35.  * have a tendency to take enormous amounts of time unless handled well.
  36.  * One finds implementations using fixed-point integers, but these
  37.  * lead to strange results where the program has a "floor" of detail, for
  38.  * instance. Using floating point prevents this flaw.
  39.  *
  40.  * The innermost loop of this function can iterate literally millions of
  41.  * times depending on the area you are viewing and the dimensions of the
  42.  * screen. Any tiny gains in this function can have significant results in
  43.  * the total compute time.
  44.  *
  45.  * So I dug out my 68882 manual and got to work. It's in ASM now in a separate
  46.  * module (another reason it's probably going to be hard to re-SASify). 
  47.  * My code is optimized to
  48.  * take advantage of the '882's capability to perform simultaneous operations.
  49.  * (Don't worry - it works just fine for an '881 - it just takes longer.)
  50.  * The big win is that there are 8 registers out there, and I can
  51.  * store a lot of values permanently in the FPU - effectively,
  52.  * they become "register double" variables.
  53.  *
  54.  * It is possible that  more juice can be squeezed out of this routine, and
  55.  * even a microsecond or two can make a big difference.
  56.  * I invite anyone who is into this sort of thing to try. I'm a programmer
  57.  * by profession, and we tend to avoid ASM like the plague because
  58.  * it is almost always too expensive to debug. (We use it for interrupt
  59.  * routines, task switchers and not a hell of a lot else.)
  60.  *
  61.  * In the mean time I've added a bunch of features. There are now a bunch
  62.  * of parameters you can specify on the command line, as follows:
  63.  *
  64.  * /r number    - specify the "r" (actually horizontal) value of the CENTER
  65.  *          of the screen. Negative is left. Values range from about
  66.  *          minus 2 to plus 2.
  67.  *
  68.  * /i number    - specify the "i" (actually vertical) value of the CENTER
  69.  *          of the screen. Negative is up. Values range from about
  70.  *          minus 1 to plus 1.
  71.  *
  72.  * /w number    - specify the width of the screen in "r" coordinate terms.
  73.  *          The vertical size is computed to maintain an aspect ratio
  74.  *          of 1.2 to 1.
  75.  *
  76.  * I made /r and /i specify the CENTER deliberately. If you can locate the
  77.  * center of a feature, it is convenient to generate the frames
  78.  * of a zoom movie, since all you have to do is change the "w" value. If
  79.  * /r and /i specified the upper left corner, that would be a royal pain.
  80.  * (The names "R" and "I" for these coordinates were established by Mandelbrot
  81.  * himself, and are maintained here for historical reasons.)
  82.  *
  83.  * If you don't specify any of the above three parameters, you get the entire
  84.  * Mandelbrot diagram.
  85.  *
  86.  * /c number    - Choose a color set. Frankly, right now I'm not very happy
  87.  *          with the ones I've chosen. If you don't specify anything,
  88.  *          you get a standard set I think works fairly well. If you
  89.  *          specify "/c 0" you get the random color set the old program
  90.  *          would have given you by default. The others are somewhat
  91.  *          obscure. There is plenty of opportunity here for enhancement.
  92.  *
  93.  * /q          Use a quarter of the screen. Actually a misnomer - this
  94.  *          divides the screen linearly by 4, so it divides the area
  95.  *          by 16. Use this when you're trying to home in on /r and /i
  96.  *          values for interesting features to cut way down on the
  97.  *          compute time.
  98.  *
  99.  * /h          Use a half (that is, 1/4 by area) of the screen.
  100.  *
  101.  * /t number      Raise the top of the drawing. The default is 255. Values
  102.  *          above 255 will cause the color set to recycle. Values can
  103.  *          be specified up to 32767. Please note that depending on the
  104.  *          image you are computing, raising this value can radically
  105.  *          slow down compute time.
  106.  *
  107.  * /d number      Often used in hand with "/t". This takes the output of the
  108.  *          function and divides it by the parameter for every pixel.
  109.  *          In essence it is lowering the color thresholds. For instance,
  110.  *          "/d 3" means that the first color threshold represents the
  111.  *          first THREE step-values, the second represents the
  112.  *          next THREE step-values, and so on. This does not alter
  113.  *          the time to compute the drawing, but it can alter its
  114.  *          image quite a bit.
  115.  *
  116.  * /s filename      If this parameter is NOT present, then when the picture is
  117.  *          completely drawn it will stay on the screen until you hit
  118.  *          the "ENTER" key. If this parameter IS present, then when
  119.  *          the image is complete it will be written out as a 24-bit
  120.  *          PPM file, and then this program will automatically terminate.
  121.  *          This means if you want to create the frames for a zoom
  122.  *          movie, you can set up a batch file and let it run overnight,
  123.  *          or for a week, or for a month, or for ...
  124.  *          WARNING: These files are ENORMOUS. You can chew up disk space
  125.  *          really fast. The file takes 3 bytes per pixel. An
  126.  *          800*600 frame is 1.5 megabytes (sigh). You might want to make
  127.  *          your batch file alternate between creating files with this
  128.  *          program and crunching them with "lha", which can typically
  129.  *          collapse such a file to 100 Kb or less, depending on
  130.  *          image complexity. Alternatively, you can use "ppmtogif"
  131.  *          which won't do as well as "lha" but leaves them in a
  132.  *          displayable form. By definition these files always have
  133.  *          fewer than 256 colors, so "ppmtogif" won't ever tell you
  134.  *          to go use "ppmquant" first.
  135.  *
  136.  * /m threshold      What happens if the Mandelbrot threshold of 4.0 is modified?
  137.  *          Beats heck out of me, but this parameter allows you to
  138.  *          experiment. (My early experimenting shows that values above
  139.  *          about 2.1 all act about the same, and below 1.9 the whole
  140.  *          image turns black.)
  141.  *
  142.  * To get you started, here are some interesting parameter values to try:
  143.  *
  144.  * /r -.93402 /i -.247638 /w .00004 /d 2 /t 512
  145.  *
  146.  * /r -.5554 /i -.5292 /w .006
  147.  *
  148.  * /r -1.2909 /i -.0861 /w .00155
  149.  *
  150.  * /r -.6763 /i -.36742 /w .0003
  151.  *
  152.  * /r -676204 /i -.367276 /w .000017
  153.  *
  154.  * /r -.6762039 /i -.3672763 /w .0000021 /d 2 /t 512    or
  155.  * /r -.6762039 /i -.3672763 /w .0000021 /d 3 /t 768
  156.  *
  157.  * Even with all the optimization, these are not going to be fast drawings,
  158.  * but the results are spectacular (in my own highly biased opinion). These
  159.  * tended to take more than half an hour on my system at 800*600. Your
  160.  * mileage may vary. (My system is a GVP G-force 68040 at 33 MHZ running
  161.  * burst mode, so a 25 MHZ 68030 is probably going to run 4 or 5 times
  162.  * as long.)
  163.  *
  164.  * If you find any interesting coordinates and feel like doing so, please send
  165.  * them to me via email at the address at the top line. Bugs are likewise of
  166.  * interest.
  167.  *
  168.  * Oh, I also added a hell of a lot of comments.
  169.  * There ARE a few comments here which are not mine, but you'll
  170.  * pick up on my style rapidly. Where there's a chance of confusion, I'll
  171.  * include my initials: SCDB.
  172.  *
  173.  * ...and for my next trick: The one obvious need here is a simple way to pick
  174.  * out the coordinate of some object, so as to center and zoom on that object.
  175.  * The obvious way to do that is to use the mouse. Well, I can't. To do that
  176.  * I have to turn on a Sprite, and the part of the Retina includes for that
  177.  * uses a standard SAS include named "utility/tagitem.h", which Aztec doesn't
  178.  * have. I can't find the equivalent structure ("struct TagItem") or define
  179.  * ("TAG_USER") anywhere in the include structure I've got and I haven't got
  180.  * the faintest idea of what they should be. If someone can tell me, I'll try
  181.  * to put it in. In the mean time, the only way to home in on objects is to use
  182.  * "/q" and to tweak the "/r" and "/i" values.
  183.  */
  184.  
  185. #define UTILITY_TAGITEM_H           /* This disables some nested includes */
  186.                      /* which Aztec doesn/t support. See above. */
  187. #include <intuition/intuitionbase.h> /* Needed to define a major structure. */
  188.                      /* SAS got it from one of the includes */
  189.                      /* files that aztec didn't have, which */
  190.                      /* I deleted. */
  191.  
  192.  
  193. #include <stdlib.h>
  194. #include <stdio.h>
  195. #include <math.h>
  196.  
  197. #include <exec/libraries.h>
  198. #include <devices/timer.h>
  199. #include <time.h>
  200.  
  201.  
  202. #include "retina/retina.h"
  203. #include "clib/retina_protos.h"
  204. #include "pragmas/retina_pragmas.h"
  205.  
  206. #define MAX_ORBITS 256
  207.  
  208.  
  209.  
  210. /* uncomment the following line to avoid repeating calculations */
  211. /* WARNING: For some reason this creates code that will cause
  212.    stack overflow errors on any image larger than 320x400 or so */
  213.  
  214. /*
  215.  * SCDB: As far as I can determine, this was a problem the original author
  216.  * encountered early and never pursued. Uncommenting this causes compile
  217.  * errors, as parts of the code which are NOT controlled by ifdefs try
  218.  * to use structure members which ARE controlled by ifdefs on this symbol.
  219.  * At this point, I consider the code controlled by this symbol to be dead,
  220.  * but I invite anyone to try to fix it if they wish. The benefit is further
  221.  * speed increases, at the expense of code complexity.
  222.  */
  223. /* #define NOREPEATCALC */
  224.  
  225. struct BrotPoint {
  226.   unsigned int x, y;
  227.   double r, i;
  228. #ifndef NOREPEATCALC
  229.   int orbits; /* -2 if untested, -1 if never escapes,
  230.          >= 0 otherwise */
  231. #endif
  232. };
  233.  
  234. /*
  235.  * SCDB: "threshold" is traditionally "4.0" as defined by Mandelbrot.
  236.  * However, I thought it might be fun to allow it to be modified, so I've
  237.  * made it a variable here whose value can be changed on the command line.
  238.  * It gets loaded by the ASM routine into fp1.
  239.  */
  240. double threshold = 4.0;
  241.  
  242. int max_orbits = MAX_ORBITS;
  243. int step_size = 1;
  244.  
  245. int screen_width, screen_height;
  246. int quarter_flag = FALSE;
  247. int half_flag = FALSE;
  248. int h_offset = 0, v_offset = 0;
  249.  
  250. void Compute_Block(struct BrotPoint, struct BrotPoint);
  251. extern short int Test_Point(double, double);
  252. void my_RectFill(unsigned int, unsigned int,
  253.                     unsigned int, unsigned int);
  254. /* version string */
  255. UBYTE vers[] = "$VER: RetinaMand as modified by SCDB, Dec. 28, 1993";
  256.  
  257. struct IntuitionBase *IntuitionBase;
  258.  
  259. struct _xy_RetinaBase *RetinaBase;
  260. struct DefaultScreenInfo screen_info;
  261.  
  262. /*
  263.  * SCDB: I haven't the faintest idea what this does. It probably doesn't do
  264.  * do anything under Aztec. Anyway, it seems to cause no harm, so I left it in.
  265.  */
  266. int __oslibversion = 37;
  267.  
  268. /*
  269.  * SCDB: The code depends on the fact that the following number is divisible by
  270.  * 3. If it ceases to be so, this program will crash horribly.
  271.  * "out_buffer" is used to accumulate large blocks of data for writing out to
  272.  * the PPM file if "/s" is used. It is a fact that programs work far more
  273.  * efficiently when they write large blocks rarely than if they write small
  274.  * blocks frequently.
  275.  */
  276. #define BUFF_SIZE 30000
  277. unsigned char    out_buffer[BUFF_SIZE];
  278.  
  279. /*
  280.  * SCDB: Later on, if we decide to save a file, we're going to have to know
  281.  * what the palette was. The following array will store those values.
  282.  */
  283. unsigned long palette[256];
  284.  
  285.  
  286. double dr, di;
  287. struct RetinaScreen *rScreen;
  288.  
  289. int main(int argc, char *argv[])
  290. {
  291.   int i;
  292.   double rstep, gstep, bstep;
  293.   int c;
  294.   UWORD x_scan, y_scan;
  295.   long    color_value;
  296.  
  297.   /*
  298.    * SCDB:
  299.    *
  300.    * "red_value" used to be named "foo".
  301.    * "green_value" used to be named "bar".
  302.    * "blue_value" used to be named "baz".
  303.    *
  304.    * Where I work this would get you hung without a trial.
  305.    *
  306.    * These also used to be UBYTEs, but you can get better palette results if
  307.    * they are floating point numbers, since this allows you fractional steps
  308.    * (which round when ultimately converted to RGB values).
  309.    *
  310.    * (I have to admit a bad habit, however: I tend to use "i", "j" and "k" for
  311.    * arbitrary loop counters when they are used very locally. Depending on how
  312.    * old you are, you may have heard of a language named "FORTRAN". Variables
  313.    * whose names began with letters between "I" and "N" were integers, with
  314.    * all other variables being floating point. It was routine to use i/j/k
  315.    * for loop-control variables. However, I never use such names when
  316.    * their scope is large. In this program I use "i", and I'm only using it
  317.    * twice, both times to step through arrays: I use it to scan 'argv' for
  318.    * parameters, and I use it to construct output buffers when writing PPMs.)
  319.    */
  320.   double red_value, green_value, blue_value;
  321.   struct BrotPoint start, end;
  322.  
  323.   /*
  324.    * The image created by this program used to be forced to the entire
  325.    * diagram. Part of my redesign is to allow a subsection to be passed in
  326.    * as invocation parameters, and to calculate and display just that part.
  327.    * That required replacing a bunch of magic numbers with variables and
  328.    * calculations in the initialization code.
  329.    */
  330.   double left_coord, top_coord, width, height;
  331.   double i_center, r_center;
  332.  
  333.   FILE *save_file = NULL;
  334.  
  335.     /*
  336.      * If there are no parameters later for a subsection, use values which
  337.      * create the entire diagram. "height" is calculated to keep the aspect
  338.      * ratio of the screen we opened.
  339.      * These are not the values used in the original program, which
  340.      * discarded the first contour, which is a large circle.
  341.      */
  342.     r_center = 0;
  343.     i_center = 0;
  344.     width = 6;
  345.  
  346.     /*
  347.      * These values are used if no color parameter is specified.
  348.      */
  349.     red_value = 0;
  350.     rstep = 4;
  351.     green_value = 0;
  352.     gstep = 2;
  353.     blue_value = 0;
  354.     bstep = 12;
  355.  
  356.     /*
  357.      * Now for something completely new: Invocation parameters!
  358.      * The parameter checking here is not quite as rigorous as it probably
  359.      * should be. Any switch using parameters only checks to see that there
  360.      * are enough entries in "argv" remaining to satisfy its needs. They
  361.      * do not check to see if they make sense. Let's assume some intelligence
  362.      * on the part of the users. There's prudence, and then there's
  363.      * paranoia.
  364.      */
  365.     for (i=1; i<argc; i++) {
  366.         if (strcmp(argv[i],"/c") == 0) {
  367.             /*
  368.              * "/c n" predefined color sets, as follows:
  369.              */
  370.             if (argc < i+1) {
  371.                 fprintf(stderr, "Not enough parameters for /c\n");
  372.                 exit(1);
  373.             }
  374.             c = atoi(argv[++i]);
  375.             switch (c) {
  376.             case 0:
  377.                 /*
  378. `                 * 0: random values, sort of. This is a direct
  379.                  * steal from the original program.
  380.                  */
  381.                 srand((long) time(NULL));
  382.                 red_value = (rand() >> 3) & 0xFF;
  383.                 green_value = (rand() >> 4) & 0xFF;
  384.                 blue_value = (rand() >> 3) & 0xFF;
  385.                 rstep = ((rand() >> 5) & 0x0F) + 1;
  386.                 bstep = ((rand() >> 5) & 0x0F) + 1;
  387.                 gstep = ((rand() >> 5) & 0x0F) + 1;
  388.                 break;
  389.             case 1:
  390.                 /*
  391.                  * 1: Gray scale, 1 slope total over the course
  392.                  * of 256 contours.
  393.                  */
  394.                 red_value = 1;
  395.                 green_value = 1;
  396.                 blue_value = 1;
  397.                 rstep = 1;
  398.                 gstep = 1;
  399.                 bstep = 1;
  400.                 break;
  401.             case 2:
  402.                 /*
  403.                  * 2: Gray scale, 4 slopes over 256 contours.
  404.                  */
  405.                 red_value = 4;
  406.                 green_value = 4;
  407.                 blue_value = 4;
  408.                 rstep = 4;
  409.                 gstep = 4;
  410.                 bstep = 4;
  411.                 break;
  412.             case 3: /*
  413.                  * 3: Gold. Properly used, this can
  414.                  * yield a picture which looks as if it glows.
  415.                  * This is why we use floating point values
  416.                  * for color construction...
  417.                  */
  418.                 red_value = 16;
  419.                 green_value = 16;
  420.                 blue_value = 16;
  421.                 rstep = 0.935;
  422.                 gstep = 0.935;
  423.                 bstep = 0.5;
  424.                 break;
  425.             default:
  426.                 fprintf(stderr, "Unknown color set %d\n", c);
  427.                 exit(1);
  428.  
  429.             }
  430.         } else if (strcmp(argv[i],"/s") == 0) {
  431.             /*
  432.              * /s filename
  433.              * store the results into that filename as a
  434.              * ppm-format file. This can get BIG.
  435.              */
  436.             if (argc < i+1) {
  437.                 fprintf(stderr, "Not enough parameters for /s\n");
  438.                 exit(1);
  439.             }
  440.             save_file = fopen(argv[++i], "w");
  441.             if (save_file == NULL) {
  442.                 fprintf(stderr,
  443.                     "Couldn't open file <%s> for output\n",
  444.                     argv[i]);
  445.                 exit(1);
  446.             }
  447.         } else if (strcmp(argv[i],"/r") == 0) {
  448.             /*
  449.              * /r CENTER of the R axis
  450.              */
  451.             if (argc < i+1) {
  452.                 fprintf(stderr, "Not enough parameters for /r\n");
  453.                 exit(1);
  454.             }
  455.             r_center = atof(argv[++i]);
  456.         } else if (strcmp(argv[i],"/w") == 0) {
  457.             /*
  458.              * /w width
  459.              */
  460.             if (argc < i+1) {
  461.                 fprintf(stderr, "Not enough parameters for /w\n");
  462.                 exit(1);
  463.             }
  464.             width = atof(argv[++i]);
  465.         } else if (strcmp(argv[i],"/i") == 0) {
  466.             /*
  467.              * /i CENTER of the I axis
  468.              */
  469.             if (argc < i+1) {
  470.                 fprintf(stderr, "Not enough parameters for /i\n");
  471.                 exit(1);
  472.             }
  473.             /*
  474.              * In order to have the same coordinate system as
  475.              * "MandelVroom" this value has to be negated.
  476.              * This allows MandelVroom to be used as a sort of
  477.              * spotter-scope for this program.
  478.              *
  479.              * ...Which is a great theory, but for reasons I don't
  480.              * understand, there is some sort of shift between
  481.              * "MandelVroom" coordinates and those used here. It is
  482.              * entirely possible that this is due to a systemic error
  483.              * I've induced in my optimized computation function.
  484.              * Anyway, "MandelVroom" gets you near, and you can
  485.              * refine it from there using /q.
  486.              */
  487.             i_center = -atof(argv[++i]);
  488.         } else if (strcmp(argv[i],"/q") == 0) {
  489.             /*
  490.              * /q - use only a quarter of the screen (per direction)
  491.              * which means a 16th of the area.
  492.              */
  493.             quarter_flag = TRUE;
  494.         } else if (strcmp(argv[i],"/h") == 0) {
  495.             /*
  496.              * /h - use only a half of the screen (per direction)
  497.              * which means a quarter of the area.
  498.              */
  499.             half_flag = TRUE;
  500.         } else if (strcmp(argv[i],"/d") == 0) {
  501.             /*
  502.              * /d - change the step-size
  503.              */
  504.             if (argc < i+1) {
  505.                 fprintf(stderr, "Not enough parameters for /d\n");
  506.                 exit(1);
  507.             }
  508.             step_size = atol(argv[++i]);
  509.             if (step_size > 126) {
  510.                 fprintf(stderr, "/d divider cannot exceed 126\n");
  511.                 exit(1);
  512.             }
  513.         } else if (strcmp(argv[i],"/t") == 0) {
  514.             /*
  515.              * /t - Raise the ceiling.
  516.              */
  517.             if (argc < i+1) {
  518.                 fprintf(stderr, "Not enough parameters for /t\n");
  519.                 exit(1);
  520.             }
  521.             max_orbits = atol(argv[++i]);
  522.             if (max_orbits > 32767) {
  523.                 fprintf(stderr, "/t cannot exceed 32767\n");
  524.                 exit(1);
  525.             }
  526.         } else if (strcmp(argv[i],"/m") == 0) {
  527.             /*
  528.              * /m threshold
  529.              * Traditionally the threshold is 4.0. But what if
  530.              * it isn't?
  531.              */
  532.             if (argc < i+1) {
  533.                 fprintf(stderr, "Not enough parameters for /m\n");
  534.                 exit(1);
  535.             }
  536.             threshold = atof(argv[++i]);
  537.         } else {
  538.             fprintf(stderr, "Unknown parameter <%s>\n", argv[i]);
  539.             exit(1);
  540.         }
  541.     }
  542.  
  543.     /*
  544.      * SCDB: Compute the height to maintain an aspect ratio of 1.2 to 1.
  545.      */
  546.     height = width * 0.83;
  547.  
  548.     /*
  549.      * SCDB: Calculate the left and top coordinates based on center, width
  550.      * and height values.
  551.      */
  552.     left_coord = r_center-width/2;
  553.     top_coord = i_center+height/2;
  554.  
  555.   if ((RetinaBase = (struct _xy_RetinaBase *)
  556.        OpenLibrary("retina.library", 2L)) != NULL) {
  557.     if ((IntuitionBase = (struct IntuitionBase *)
  558.      OpenLibrary("intuition.library", 37L)) != NULL) {
  559.       if ((rScreen = Retina_OpenScreen(RSCR_STDWIDTH, RSCR_STDHEIGHT,
  560.                        MID_DEFAULT_08,
  561.                        RSFF_AUTOADJUST, 0L))
  562.       != NULL) {
  563.     printf("Screen Opened successfully: %d by %d\n",
  564.            rScreen->_rs_Width, rScreen->_rs_Height);
  565.  
  566.     /*
  567.      * SCDB: Well, ONE of the following flags has to have precedence. If the
  568.      * user is silly enough to specify both, we'll use the smaller one.
  569.      */
  570.     if (quarter_flag) {
  571.         screen_width  = rScreen->_rs_Width/4;
  572.         h_offset      = (rScreen->_rs_Width*3)/8; /* to center it */
  573.         screen_height = rScreen->_rs_Height/4;
  574.         v_offset      = (rScreen->_rs_Height*3)/8; /* to center it */
  575.     } else if (half_flag) {
  576.         screen_width  =  rScreen->_rs_Width/2;
  577.         h_offset      = rScreen->_rs_Width/4; /* to center it */
  578.         screen_height =  rScreen->_rs_Height/2;
  579.         v_offset      = rScreen->_rs_Height/4; /* etc. */
  580.     } else {
  581.         screen_width = rScreen->_rs_Width;
  582.         screen_height = rScreen->_rs_Height;
  583.     }
  584.  
  585.  
  586.     /* set up screen colors; color 0 must always be black */
  587.     Retina_SetPalette(rScreen, 0, 0, 0, 0);
  588.     palette[0] = 0;
  589.     for (i = 1; i < 256; i++) {
  590.       palette[i] = ((long)red_value<<16)+((long)green_value<<8)+(long)blue_value;
  591.       Retina_SetPalette(rScreen, i, red_value, green_value, blue_value);
  592.       red_value += rstep;
  593.       green_value += gstep;
  594.       blue_value += bstep;
  595.     }
  596.  
  597.     Retina_SetDrMd(rScreen, RDM_JAM1);
  598.  
  599.     /*
  600.      * SCDB: I deduce that 'dr' is the the numeric increment in
  601.      * calculation-space represented by one pixel to the right, and
  602.      * 'di' is the numeric increment in calculation-sapce of one pixel down.
  603.      */
  604.     dr = (width)/((double) screen_width);
  605.     di = -(height)/((double) screen_height);
  606.  
  607.     /* OK, folks, enough initializing.  Let's call the
  608.        Mbrot routine. */
  609.  
  610.     /*
  611.      * SCDB:
  612.      * The screen is created in four quadrants. Create the upper left
  613.      * quadrant first.
  614.      * The parameters are:
  615.      *    "x" is the pixel position horizontally.
  616.      *    "y" is the pixel position vertically.
  617.      *    "r" is the value in calculation-space horizontally.
  618.      *        (0 to the left)
  619.      *    "i" is the value in calculation-space vertically.
  620.      *        (0 below, but we inverted the parameter so 0 is
  621.      *         effectively above)
  622.      *
  623.      * The original code used incremental changes on the structures
  624.      * to set up these values, but in the interest of clarity
  625.      * I'm going to do them completely for each quadrant. The
  626.      * increase in execution time is miniscule.
  627.      *
  628.      * A long description of a change I made: I don't think that the
  629.      * original code handled this correctly. Both axes were handled the same
  630.      * way, so I'll describe only one of them for simplicity's sake. The
  631.      * original code described the first quadrant as 0-(width>>1) and the
  632.      * second as (width>>1)-width; which bothered me because
  633.      * different screen pixels were used for the last of the first
  634.      * quadrant and the first of the other quadrant, but they were computed
  635.      * using the same value. I modified this so that the first quadrant goes
  636.      * 0-((width>>1)-1) but I think I missed something in the logic.
  637.      * As I've observed it writing the screen, it 
  638.      * sometimes redundantly writes over pixels it has previously calculated
  639.      * BUT WITH DIFFERENT COLORS. There's no way that can be correct.
  640.      * The result is still esthetically pleasing and shows no important
  641.      * gaps or asymmetries, so I haven't pursued it. I don't understand the
  642.      * code which recursively divides squares below this point, and the
  643.      * problem lies in there somewhere.
  644.      * This is another invitation for change to anyone interested.
  645.      *
  646.      * First describe the pixel set of the quadrant
  647.      */
  648.     start.x = 0;
  649.     end.x = (screen_width >> 1)-1;
  650.     start.y = 0;
  651.     end.y = (screen_height >> 1)-1;
  652.  
  653.     /*
  654.      * Then derive the numeric set for the quadrant.
  655.      * This is the same for every quadrant. I've described it this way
  656.      * because it has negligible effect on execution time, but makes the
  657.      * code clearer.
  658.      */
  659.     start.r = left_coord + dr*(double)start.x;
  660.     end.r = left_coord + dr*(double)end.x;
  661.     start.i = top_coord + di*(double)start.y;
  662.     end.i = top_coord + di*(double)end.y;
  663.  
  664.  
  665. #ifndef NOREPEATCALC
  666.     start.orbits = -2;
  667.     end.orbits = -2;
  668. #endif
  669.     Compute_Block(start, end);
  670.  
  671.  
  672.     /*
  673.      * Create the lower left quadrant.
  674.      */
  675.     start.x = 0;
  676.     end.x = (screen_width >> 1)-1;
  677.     start.y = (screen_height>>1);
  678.     end.y = screen_height-1;
  679.  
  680.     start.r = left_coord + dr*(double)start.x;
  681.     end.r = left_coord + dr*(double)end.x;
  682.     start.i = top_coord + di*(double)start.y;
  683.     end.i = top_coord + di*(double)end.y;
  684.  
  685.     Compute_Block(start, end);
  686.  
  687.     /*
  688.      * Create the lower right quadrant.
  689.      */
  690.     start.x = (screen_width>>1);
  691.     end.x = screen_width-1;
  692.     start.y = (screen_height>>1);
  693.     end.y = screen_height-1;
  694.  
  695.     start.r = left_coord + dr*(double)start.x;
  696.     end.r = left_coord + dr*(double)end.x;
  697.     start.i = top_coord + di*(double)start.y;
  698.     end.i = top_coord + di*(double)end.y;
  699.  
  700.     Compute_Block(start, end);
  701.  
  702.     /*
  703.      * Create the upper right quadrant;
  704.      */
  705.     start.x = (screen_width>>1);
  706.     end.x = screen_width-1;
  707.     start.y = 0;
  708.     end.y = (screen_height >> 1)-1;
  709.  
  710.     start.r = left_coord + dr*(double)start.x;
  711.     end.r = left_coord + dr*(double)end.x;
  712.     start.i = top_coord + di*(double)start.y;
  713.     end.i = top_coord + di*(double)end.y;
  714.  
  715.     Compute_Block(start, end);
  716.  
  717.  
  718.     if (save_file != NULL) {
  719.         /*
  720.          * Then the user wants the results saved to a PPM file.
  721.          * The file has already been opened, so the only thing we
  722.          * have to do is write it out.
  723.          *
  724.          * The first part of a PPM is "P6" to indicate that
  725.          * it IS a PPM, the x and y sizes and "255" to
  726.          * indicate full brightness level. This is then followed
  727.          * by byte triples (R,G,B) scanning x within y.
  728.          * It took me a while to realize that the placement of spaces
  729.          * and newlines here is critical for some programs which are
  730.          * picky.
  731.          */
  732.         fprintf(save_file, "P6\n%d %d\n255\n",
  733.             screen_width, screen_height);
  734.         i = 0;
  735.         for (y_scan=0; y_scan<screen_height; y_scan++) {
  736.             for (x_scan=0; x_scan<screen_width; x_scan++) {
  737.  
  738.             /*
  739.              * The documentation says that Retina_ReadPixel returns
  740.              * the RGB value. Actually for 8 bit screens it returns
  741.              * the palette index for that pixel.
  742.              * We therefore have to translate
  743.              * the palette index into RGB based on the values we
  744.              * saved when we set the palette up in the first place.
  745.              */
  746.             color_value = Retina_ReadPixel(rScreen,
  747.                 x_scan+h_offset, y_scan+v_offset);
  748.             color_value = palette[color_value];
  749.  
  750.             out_buffer[i++] = (unsigned char)
  751.                 ((color_value >> 16) & 0xFFL);
  752.             out_buffer[i++] = (unsigned char)
  753.                 ((color_value >> 8) & 0xFFL);
  754.             out_buffer[i++] = (unsigned char)(color_value & 0xFFL);
  755.  
  756.             /*
  757.              * The output has been accumulated in a big buffer
  758.              * because programs run faster if they write out a few
  759.              * big blocks than if they write out a lot of small
  760.              * ones, which is what would have happened if we'd
  761.              * been using "fwrite" on the color values directly.
  762.              */
  763.             if (i>=BUFF_SIZE) {
  764.                 fwrite(out_buffer, i, 1, save_file);
  765.                 i = 0;
  766.             }
  767.             }
  768.         }
  769.  
  770.         /*
  771.          * We've put all values into the output buffer, and most of
  772.          * it has probably been written out. If there is any residue,
  773.          * write it out, too.
  774.          */
  775.         if (i != 0)
  776.             fwrite(out_buffer, i, 1, save_file);
  777.         fclose(save_file);
  778.  
  779.     } else {
  780.  
  781.         /*
  782.          * If no output file was specified, leave the picture on the
  783.          * screen until the user indicates it should go away.
  784.          * For people (like me) who use retina workbench emulation,
  785.          * the following printf is useless - but
  786.          * there might be someone out there who is keeping
  787.          * their workbench on something else. The rest of us are just
  788.          * going to have to remember to hit CR afterwards.
  789.          *
  790.          * In theory, any keypress should work. In practice, "getchar"
  791.          * (at least for Aztec) stacks up all input until CR is pressed
  792.          * and only presents it to the program at that point.
  793.          */
  794.         printf("Press -- ENTER -- to quit.\n"); 
  795.         getchar();
  796.     }
  797.  
  798.     Retina_CloseScreen(rScreen);
  799.       }
  800.       else {
  801.     fprintf(stderr, "Unable to open Retina screen.\n");
  802.       }
  803.     }
  804.     else {
  805.       fprintf(stderr, "Can't open intuition.library V37+\n");
  806.     }
  807.   }
  808.   else {
  809.     fprintf(stderr, "Can not open retina.library!\n");
  810.   }
  811.   exit(0);
  812. }
  813.  
  814. /*
  815.  * SCDB: I do not understand the following code and have made no attempt
  816.  * to debug or optimize it. Heare bee dragones.
  817.  */
  818.  
  819. void Compute_Block(struct BrotPoint uleft, struct BrotPoint lright)
  820. {
  821.   unsigned int i;
  822.   double j;
  823.   short int q, z;
  824.  
  825.   if (lright.x - uleft.x < 2 && lright.y - uleft.y < 2) {
  826. #ifdef NOREPEATCALC
  827.     q = Test_Point(uleft.r, uleft.i)/step_size;
  828.     if (q > 0) Retina_SetPixel(rScreen, uleft.x+h_offset, uleft.y+v_offset, q);
  829. #else
  830.     if (uleft.orbits == -2) {
  831.       uleft.orbits = Test_Point(uleft.r, uleft.i)/step_size;
  832.  
  833.     }
  834.     if (uleft.orbits > 0) Retina_SetPixel(rScreen, uleft.x+h_offset,
  835.                 uleft.y+v_offset, uleft.orbits);
  836. #endif
  837.     q = Test_Point(uleft.r, lright.i)/step_size;
  838.  
  839.     if (q > 0) Retina_SetPixel(rScreen, uleft.x+h_offset, lright.y+v_offset, q);
  840. #ifdef NOREPEATCALC
  841.     q = Test_Point(lright.r, lright.i)/step_size;
  842.     if (q > 0) Retina_SetPixel(rScreen, lright.x+h_offset, lright.y+v_offset, q);
  843. #else
  844.     if (lright.orbits == -2) {
  845.       lright.orbits = Test_Point(lright.r, lright.i)/step_size;
  846.     }
  847.  
  848.     if (lright.orbits > 0) Retina_SetPixel(rScreen, lright.x+h_offset,
  849.                 lright.y+v_offset, lright.orbits);
  850. #endif
  851.     q = Test_Point(lright.r, uleft.i)/step_size;
  852.  
  853.     if (q > 0) Retina_SetPixel(rScreen, lright.x+h_offset, uleft.y+v_offset, q);
  854.     return;
  855.   }
  856.  
  857.   /* draw a box -- if the borders are all the same, then fill it that
  858.      color */
  859.   q = Test_Point(uleft.r, uleft.i)/step_size;
  860.  
  861.   uleft.orbits = q;
  862.   if (q > 0)
  863.     Retina_SetPixel(rScreen, uleft.x+h_offset, uleft.y+v_offset, q);
  864.   z = Test_Point(lright.r, lright.i)/step_size;
  865.  
  866.   lright.orbits = z;
  867.   if (z > 0)
  868.     Retina_SetPixel(rScreen, lright.x+h_offset, lright.y+v_offset, z);
  869.   if (z != q) {
  870.     goto recurse;
  871.   }
  872.  
  873.   /* top & bottom */
  874.   for (i = uleft.x, j = uleft.r; i <= lright.x; i++, j += dr) {
  875.     z = Test_Point(j, uleft.i)/step_size;
  876.  
  877.     if (z != q) {
  878.       goto recurse;
  879.     }
  880.     else {
  881.       if (z > 0)
  882.     Retina_SetPixel(rScreen, i+h_offset, uleft.y+v_offset, z);
  883.     }
  884.     z = Test_Point(j, lright.i)/step_size;
  885.  
  886.     if (z != q) {
  887.       goto recurse;
  888.     }
  889.     else {
  890.       if (z > 0)
  891.     Retina_SetPixel(rScreen, i+h_offset, lright.y+v_offset, z);
  892.     }
  893.   }
  894.   /* left and right */
  895.   for (i = uleft.y, j = uleft.i; i <= lright.y; i++, j += di) {
  896.     z = Test_Point(uleft.r, j)/step_size;
  897.  
  898.     if (z != q) {
  899.       goto recurse;
  900.     }
  901.     else {
  902.       if (z > 0)
  903.     Retina_SetPixel(rScreen, uleft.x+h_offset, i+v_offset, z);
  904.     }
  905.     z = Test_Point(lright.r, j)/step_size;
  906.  
  907.     if (z != q) {
  908.       goto recurse;
  909.     }
  910.     else {
  911.       if (z > 0)
  912.     Retina_SetPixel(rScreen, lright.x+h_offset, i+v_offset, z);
  913.     }
  914.   }
  915.  
  916.   /* if we passed through all that, we draw a filled rectangle */
  917.   if (q > 0) {
  918.     Retina_SetAPen(rScreen, q);
  919.  
  920.     my_RectFill(uleft.x, uleft.y, lright.x, lright.y);
  921.   }
  922.   return;
  923.  
  924.  recurse:
  925.   {
  926.     struct BrotPoint top, bot, left, right, middle;
  927.  
  928.     top.x = bot.x = middle.x = (uleft.x + lright.x) >> 1;
  929.     left.y = right.y = middle.y = (uleft.y + lright.y) >> 1;
  930.     top.r = bot.r = middle.r = (uleft.r + lright.r)/2.0;
  931.     left.i = right.i = middle.i = (uleft.i + lright.i)/2.0;
  932.     top.y = uleft.y;
  933.     top.i = uleft.i;
  934.     bot.y = lright.y;
  935.     bot.i = lright.i;
  936.     left.x = uleft.x;
  937.     left.r = uleft.r;
  938.     right.x = lright.x;
  939.     right.r = lright.r;
  940. #ifndef NOREPEATCALC
  941.     middle.orbits = top.orbits = bot.orbits = left.orbits =
  942.       right.orbits = -2;
  943. #endif
  944.  
  945.     Compute_Block(uleft, middle);
  946. #ifdef NOREPEATCALC
  947.     left.y += 1;
  948.     left.i += di;
  949. #endif
  950.     Compute_Block(left, bot);
  951. #ifdef NOREPEATCALC
  952.     middle.x += 1;
  953.     middle.y += 1;
  954.     middle.r += dr;
  955.     middle.i += di;
  956.     top.x += 1;
  957.     top.r += dr;
  958. #endif
  959.     Compute_Block(middle, lright);
  960.     Compute_Block(top, right);
  961.   }
  962. }
  963.  
  964. /*
  965.  * SCDB: The routine which follows used to be controlled by an ifdef. I've
  966.  * made it unconditional, since it works very well.
  967.  * "my" is the original author, not me. He did good.
  968.  */
  969. void my_RectFill(unsigned int x1, unsigned int y1,
  970.                     unsigned int x2, unsigned int y2)
  971. {
  972.   short int tx1, tx2, v;
  973.  
  974.   /* a little magic because Retina_RectFill() ignores the low-order
  975.      3 bits of the x-coords for 256-color screens */
  976.   tx1 = (x1 >> 3) + 1; tx2 = (x2 >> 3) - 1;
  977.   tx1 <<= 3;
  978.   tx2 <<= 3;
  979.  
  980.   if (tx2 > tx1) {
  981.     Retina_RectFill(rScreen, tx1+h_offset, y1+v_offset, tx2+h_offset, y2+v_offset);
  982.     if (x2 - tx2 > 1) {
  983.       for (v = tx2; v <= x2; v++)
  984.     Retina_Line(rScreen, v+h_offset, y1+v_offset, v+h_offset, y2+v_offset);
  985.     }
  986.     if (tx1 - x1 > 1) {
  987.       for (v = x1; v <= tx1; v++)
  988.     Retina_Line(rScreen, v+h_offset, y1+v_offset, v+h_offset, y2+v_offset);
  989.     }
  990.   }
  991.   else {
  992.     /* fake it with line drawing */
  993.     for (v = y1; v <= y2; v++) {
  994.       Retina_Line(rScreen, x1+h_offset, v+v_offset, x2+h_offset, v+v_offset);
  995.     }
  996.   }
  997. }
  998.  
  999. #ifdef UNUSED
  1000. /*
  1001.  * SCDB: Sorry folks, a bit of a debugging hook. I never got the hang of the
  1002.  * Aztec debugger, powerful though it is, since I've never really had the need.
  1003.  * My traditional way of debugging is to sprinkle printf's all through the code
  1004.  * to figure what the hell is happening. These are mechanisms which can
  1005.  * be called from the FPU routine to see what's happening there.
  1006.  * Unless further debugging is required there by someone using as primitive
  1007.  * of techniques as I used, they are unnecessary.
  1008.  */
  1009. int    trace_buff[32] = {-1,-1,-1,-1,-1,-1,-1,-1
  1010.               -1,-1,-1,-1,-1,-1,-1,-1,
  1011.               -1,-1,-1,-1,-1,-1,-1,-1,
  1012.               -1,-1,-1,-1,-1,-1,-1,-1};
  1013.  
  1014. int    trace_index=0;
  1015.  
  1016. void trace(val)
  1017. int val;
  1018. {
  1019.     trace_buff[trace_index] = val;
  1020.     if (++trace_index >= 32)
  1021.         trace_index = 0;
  1022. }
  1023.  
  1024. void trace_dump()
  1025. {
  1026.     int    i;
  1027.  
  1028.     for (i=trace_index; i<32; i++)
  1029.         printf("%d: %d\n", i, trace_buff[i]);
  1030.  
  1031.     for (i=0; i<trace_index; i++)
  1032.         printf("%d: %d\n", i, trace_buff[i]);
  1033. }
  1034. #endif
  1035.